﻿using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading;

namespace ServerCrafter.ClassLibrary
{
    public static class Server
    {
        // The server process
        public static Process Process;

        /// <summary>
        /// Returns true if server is running and false if it is not.
        /// </summary>
        public static bool IsRunning;

        /// <summary>
        /// Returns true if a restart has been requested and false if not.
        /// </summary>
        public static bool RestartRequested;

        /// <summary>
        /// The server process ID.
        /// </summary>
        public static int Pid;

        /// <summary>
        /// The thread that starts the server.
        /// </summary>
        private static void ServerThread()
        {
            Logger.WriteInfo("Server thread has started.");
            Logger.WriteInfo("Starting server process on the newly created thread.");

            try
            {
                var arguments = "-jar -Xms" + Settings.ServerStartInfo.InitialRam + "M -Xmx" +
                                Settings.ServerStartInfo.MaximumRam + "M \"" + Settings.ServerStartInfo.FileName +
                                "\" -nojline" + Settings.ServerStartInfo.Arguments; 

                var processStartInfo = new ProcessStartInfo
                    {
                        FileName = "javaw.exe",
                        Arguments = arguments,
                        CreateNoWindow = true,
                        ErrorDialog = false,
                        RedirectStandardOutput = true,
                        RedirectStandardError = true,
                        RedirectStandardInput = true,
                        StandardOutputEncoding = Encoding.UTF8,
                        StandardErrorEncoding = Encoding.UTF8,
                        UseShellExecute = false,
                        WorkingDirectory = Settings.ServerStartInfo.WorkingDirectory
                    };

                Process = new Process { StartInfo = processStartInfo };
                Process.OutputDataReceived += ServerOutputHandler.ServerOutputReceived;
                Process.ErrorDataReceived += ServerOutputHandler.ServerOutputReceived;
                Process.Start();

                Pid = Process.Id;
                IsRunning = true;
                Logger.WriteInfo("The process as been started.");

                Process.BeginOutputReadLine();
                Process.BeginErrorReadLine();
                Process.WaitForExit();

                Pid = 0;
                IsRunning = false;
                Logger.WriteInfo("The server process has exited.");

                if (!RestartRequested) return;

                Logger.WriteInfo("Restarting the server process.");
                RestartRequested = false;
                Process.Start();
            }
            catch (Win32Exception exception)
            {
                Logger.WriteSevere(
                    "The process could not be started. There was an error in opening the associated file.");
                Logger.WriteSevere("\tException: " + exception);
            }
            catch (ObjectDisposedException exception)
            {
                Logger.WriteSevere("The process could not be started. The process object has already been disposed.");
                Logger.WriteSevere("\tException: " + exception);
            }
            catch (InvalidOperationException exception)
            {
                Logger.WriteSevere("The process could not be started. No file name is specified.");
                Logger.WriteSevere("\tException: " + exception);
            }
        }

        /// <summary>
        /// Gets the server Uptime.
        /// </summary>
        /// <returns></returns>
        public static TimeSpan Uptime()
        {
            return DateTime.Now - Process.StartTime;
        }

        /// <summary>
        /// Commands that can be give to the server.
        /// </summary>
        public static class Controls
        {
            /// <summary>
            /// Starts the server thread.
            /// </summary>
            public static void Start()
            {
                Logger.WriteInfo("Starting server thread.");
                try
                {
                    var serverThreadStart = new ThreadStart(ServerThread);
                    var serverThread = new Thread(serverThreadStart);
                    serverThread.Start();
                }
                catch (ThreadStateException exception)
                {
                    Logger.WriteSevere("The thread could not be started. The thread has already been started.");
                    Logger.WriteSevere("\tException: " + exception);
                }
                catch (OutOfMemoryException exception)
                {
                    Logger.WriteSevere(
                        "The thread could not be started. There is not enough memory available to start this thread.");
                    Logger.WriteSevere("\tException: " + exception);
                }
                catch (Exception exception)
                {
                    Logger.WriteSevere("The thread could not be started. An unknown exception occured.");
                    Logger.WriteSevere("\tException: " + exception);
                }
            }

            /// <summary>
            /// Kills this server.
            /// </summary>
            public static void Kill()
            {
                Process.Kill();
            }

            /// <summary>
            /// Gives the specified command.
            /// </summary>
            /// <param name="command">The command.</param>
            public static void Give(string command)
            {
                Logger.WriteInfo("Inputing command to server: " + command + ".");
                try
                {
                    Process.StandardInput.WriteLine(command);
                }
                catch (IOException exception)
                {
                    Logger.WriteInfo("Could not input command to server. An I/O error has occured.");
                    Logger.WriteInfo("\tException: " + exception);
                }
                catch (Exception exception)
                {
                    Logger.WriteInfo("Could not input command to server. An unknown exception has occured.");
                    Logger.WriteInfo("\tException: " + exception);
                }
            }

            /// <summary>
            /// Stops this server.
            /// </summary>
            public static void Stop()
            {
                Give("stop");
            }

            /// <summary>
            /// Restarts this server.
            /// </summary>
            public static void Restart()
            {
                RestartRequested = true; Give("stop");
            }

            /// <summary>
            /// Reloads this server's plugins.
            /// </summary>
            public static void Reload()
            {
                Give("reload");
            }

            /// <summary>
            /// Saves this server.
            /// </summary>
            public static void Save()
            {
                Give("save-all");
            }
        }

        public static class Options
        {
            public static bool IsSayChecked { get; set; }
        }
    }
}